/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | www.openfoam.com
     \\/     M anipulation  |
-------------------------------------------------------------------------------
    Copyright (C) 2011-2016 OpenFOAM Foundation
    Copyright (C) 2016-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

    OpenFOAM is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.

Class
    Foam::HashSet

Description
    A HashTable with keys but without contents that is similar to
    \c std::unordered_set.

    The entries are considered \a unordered since their placement
    depends on the method used to generate the hash key index, the
    table capacity, insertion order etc. When the key order is
    important, use the sortedToc() method to obtain a list of sorted
    keys and use that for further access.

Note
    The HashSet iterator dereferences to the key, so the following
    range-for works as expected:
    \code
        labelHashSet someLabels{10, 20, 30, 40, ...};
        for (const label i : someLabels)
        {
            Info<< "val:" << i << nl;
        }
    \endcode

Typedef
    Foam::wordHashSet

Description
    A HashSet with (the default) word keys.

Typedef
    Foam::labelHashSet

Description
    A HashSet with label keys and label hasher.

\*---------------------------------------------------------------------------*/

#ifndef HashSet_H
#define HashSet_H

#include "HashTable.H"
#include "IndirectList.H"

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace Foam
{

// Forward Declarations
template<class T> class MinMax;

/*---------------------------------------------------------------------------*\
                           Class HashSet Declaration
\*---------------------------------------------------------------------------*/

template<class Key=word, class Hash=string::hash>
class HashSet
:
    public HashTable<zero::null, Key, Hash>
{
    // Private Member Functions

        //- Assign from the input iterator range
        template<class InputIter>
        inline label assignMany
        (
            const label nItems,
            InputIter first,
            InputIter last
        );


public:

    //- The template instance used for this HashSet
    typedef HashSet<Key, Hash> this_type;

    //- The template instance used for the parent HashTable
    typedef HashTable<zero::null, Key, Hash> parent_type;

    //- An iterator, returning reference to the key
    using iterator = typename parent_type::key_iterator;

    //- A const_iterator, returning reference to the key
    using const_iterator = typename parent_type::const_key_iterator;


    // Constructors

        //- Default construct with default (128) table capacity
        HashSet()
        :
            parent_type()
        {}

        //- Copy construct
        HashSet(const this_type& rhs)
        :
            parent_type(rhs)
        {}

        //- Move construct
        HashSet(this_type&& rhs)
        :
            parent_type(std::move(rhs))
        {}

        //- Construct given initial table capacity
        explicit HashSet(const label size)
        :
            parent_type(size)
        {}

        //- Construct from Istream with default table capacity
        explicit HashSet(Istream& is)
        :
            parent_type(is)
        {}

        //- Construct from FixedList of Key
        template<unsigned N>
        explicit HashSet(const FixedList<Key, N>& list);

        //- Construct from UList of Key
        explicit HashSet(const UList<Key>& list);

        //- Construct from an indirect list
        template<class Addr>
        explicit HashSet(const IndirectListBase<Key, Addr>& list);

        //- Construct from an initializer list of Key
        HashSet(std::initializer_list<Key> list);

        //- Construct from the keys of another HashTable,
        //- the type of values held is arbitrary.
        template<class AnyType, class AnyHash>
        explicit HashSet(const HashTable<AnyType, Key, AnyHash>& tbl);


    // Member Functions

        //- Same as found() - return true if key exists in the set.
        //  Method name compatibility with bitSet and boolList.
        bool test(const Key& key) const noexcept
        {
            return found(key);
        }


    // Edit

        //- Insert a new entry, not overwriting existing entries.
        //  \return True if the entry inserted, which means that it did
        //  not previously exist in the set.
        bool insert(const Key& key)
        {
            return this->parent_type::emplace(key);
        }

        //- Same as insert (no value to overwrite)
        bool set(const Key& key)
        {
            return insert(key);
        }

        //- Unset the specified key - same as erase
        //  \return True if the entry existed and was removed
        bool unset(const Key& key)
        {
            return this->parent_type::erase(key);
        }


    // Convenience

        //- Insert keys from the input iterator range
        //  \return The number of new elements inserted
        template<class InputIter>
        inline label insert(InputIter first, InputIter last);

        //- Insert keys from a initializer list of Key
        //  \return The number of new elements inserted
        inline label insert(std::initializer_list<Key> list);

        //- Insert keys from the list of Key
        //  \return The number of new elements inserted
        template<unsigned N>
        inline label insert(const FixedList<Key, N>& list);

        //- Insert keys from the list of Key
        //  \return The number of new elements inserted
        inline label insert(const UList<Key>& list);

        //- Insert keys from the list of Key
        //  \return The number of new elements inserted
        template<class Addr>
        inline label insert(const IndirectListBase<Key, Addr>& list);

        //- Same as insert (no value to overwrite)
        template<class InputIter>
        inline label set(InputIter first, InputIter last)
        {
            return insert(first, last);
        }

        //- Same as insert (no value to overwrite)
        inline label set(std::initializer_list<Key> list)
        {
            return insert(list);
        }

        //- Same as insert (no value to overwrite)
        template<unsigned N>
        inline label set(const FixedList<Key, N>& list)
        {
            return insert(list);
        }

        //- Same as insert (no value to overwrite)
        inline label set(const UList<Key>& list)
        {
            return insert(list);
        }

        //- Same as insert (no value to overwrite)
        template<class Addr>
        inline label set(const IndirectListBase<Key, Addr>& list)
        {
            return insert(list);
        }

        //- Same as insert (no value to overwrite)
        //  \note Method name compatibility with bitSet
        template<class InputIter>
        inline label setMany(InputIter first, InputIter last)
        {
            return insert(first, last);
        }

        //- Unset the keys listed in the input iterator range
        //  \return The number of items removed
        template<class InputIter>
        inline label unset(InputIter first, InputIter last);

        //- Unset the listed keys - same as erase
        //  \return The number of items removed
        inline label unset(std::initializer_list<Key> list);

        //- Unset the listed keys - same as erase
        //  \return The number of items removed
        template<unsigned N>
        inline label unset(const FixedList<Key, N>& list);

        //- Unset the listed keys - same as erase
        //  \return The number of items removed
        inline label unset(const UList<Key>& list);

        //- Unset the listed keys - same as erase
        //  \return The number of items removed
        template<class Addr>
        inline label unset(const IndirectListBase<Key, Addr>& list);


    // STL iterators

        inline iterator begin();
        inline const_iterator begin() const;
        inline const_iterator cbegin() const;

        inline iterator end() noexcept;
        inline const_iterator end() const noexcept;
        inline constexpr const_iterator cend() const noexcept;


    // Writing

        //- Write unordered keys (list), with line-breaks
        //- when length exceeds shortLen.
        //  Using '0' suppresses line-breaks entirely.
        Ostream& writeList(Ostream& os, const label shortLen=0) const
        {
            return this->writeKeys(os, shortLen);
        }


    // Member Operators

        //- Return true if the entry exists, same as found()
        inline bool operator()(const Key& key) const noexcept;

        //- Return true if the entry exists, same as found().
        inline bool operator[](const Key& key) const noexcept;

        using parent_type::operator=;

        //- Copy assignment
        void operator=(const this_type& rhs)
        {
            parent_type::operator=(rhs);
        }

        //- Move assignment
        void operator=(this_type&& rhs)
        {
            parent_type::operator=(std::move(rhs));
        }


    // Comparison

        //- Sets are equal if all keys are equal,
        //- independent of order or underlying storage size.
        bool operator==(const this_type& rhs) const;

        //- The opposite of the equality operation.
        bool operator!=(const this_type& rhs) const;


    // Assignment

        //- Assignment from a UList of keys
        void operator=(const UList<Key>& rhs);

        //- Assignment from a FixedList of keys
        template<unsigned N>
        void operator=(const FixedList<Key, N>& rhs);

        //- Assignment from an initializer list of keys
        void operator=(std::initializer_list<Key> rhs);


    // Logical and set operations

        //- Add entries to this HashSet
        this_type& operator|=(const this_type& rhs);

        //- Only retain entries found in both HashSets
        inline this_type& operator&=(const this_type& rhs);

        //- Only retain unique entries (xor)
        this_type& operator^=(const this_type& rhs);

        //- Add entries to this HashSet. Same as the '|=' operator
        inline this_type& operator+=(const this_type& rhs);

        //- Remove entries from this HashSet. Uses erase()
        inline this_type& operator-=(const this_type& rhs);


    // Housekeeping

        //- Not applicable for HashSet
        template<class UnaryPredicate>
        List<Key> tocValues(const UnaryPredicate&, const bool) = delete;

        //- Not applicable for HashSet
        template<class BinaryPredicate>
        List<Key> tocEntries(const BinaryPredicate&, const bool) = delete;

        //- Not applicable for HashSet
        template<class UnaryPredicate>
        label countValues(const UnaryPredicate&, const bool) = delete;

        //- Not applicable for HashSet
        template<class BinaryPredicate>
        label countEntries(const BinaryPredicate&, const bool) = delete;

        //- Not applicable for HashSet
        template<class UnaryPredicate>
        label filterValues(const UnaryPredicate&, const bool) = delete;

        //- Not applicable for HashSet
        template<class BinaryPredicate>
        label filterEntries(const BinaryPredicate&, const bool) = delete;
};


// Typedefs

//- A HashSet with word keys.
typedef HashSet<word> wordHashSet;

//- A HashSet with label keys and label hasher.
typedef HashSet<label, Hash<label>> labelHashSet;


// Global Functions

//- Find the min value in labelHashSet, optionally limited by second argument.
//  For an empty set, returns the second argument (eg, labelMax).
label min(const labelHashSet& set, label minValue = labelMax);

//- Find the max value in labelHashSet, optionally limited by second argument.
//  For an empty set, returns the second argument (eg, labelMin).
label max(const labelHashSet& set, label maxValue = labelMin);

//- Find the min/max values of labelHashSet
MinMax<label> minMax(const labelHashSet& set);


// Global Operators

//- Write the list of HashSet keys
template<class Key, class Hash>
Ostream& operator<<(Ostream& os, const HashSet<Key, Hash>& tbl);


//- Combine entries from HashSets
template<class Key, class Hash>
HashSet<Key, Hash> operator|
(
    const HashSet<Key, Hash>& hash1,
    const HashSet<Key, Hash>& hash2
);


//- Create a HashSet that only contains entries found in both HashSets
template<class Key, class Hash>
HashSet<Key, Hash> operator&
(
    const HashSet<Key, Hash>& hash1,
    const HashSet<Key, Hash>& hash2
);


//- Create a HashSet that only contains unique entries (xor)
template<class Key, class Hash>
HashSet<Key, Hash> operator^
(
    const HashSet<Key, Hash>& hash1,
    const HashSet<Key, Hash>& hash2
);


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

} // End namespace Foam

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#ifdef NoRepository
    #include "HashSet.C"
#endif

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#endif

// ************************************************************************* //
